/*
  THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
  ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED
  TO THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
  PARTICULAR PURPOSE.

  Copyright (c) 2005-2009 PRO ACTIVE SAS - www.proactive-group.com
   
  Ref_Smartcard.c
  ---------------

  This is the reference application that show how to connect to
  a CSB5 or SpringProx-RC or whatever product featuring a GemCore
  for smarcards (and SAMs) operation
  
  JDA 21/11/2005 : initial release
  JDA 10/01/2007 : fixed a few warnings when compiling under GCC

*/
#include "products/springprox/springprox.h"

#include <stdio.h>
#include <time.h>

void show_status(BYTE slot, BYTE stat, BYTE type, BYTE * config)
{
  if (stat & 0x04)
  {
    /* Card inserted */
    /* ------------- */

    /* Hum... on SIM/SAM slots this bit is always set because there's no hardware detection switch */

    if (stat & 0x02)
    {
      /* Card powered */
      if (slot == 0)
      {
        /* On main slot we can retrieve the class */
        if (stat & 0x01)
        {
          printf("\tCard inserted and powered, class B (3V)\n");
        } else
        {
          printf("\tCard inserted and powered, class A (5V)\n");
        }
      } else
      {
        /* On SIM/SAM slot, we can't trust the class, it is always B */
        printf("\tCard inserted and powered\n");
      }

      /* Active protocol is either T=0 or T=1 */
      if (stat & 0x08)
      {
        printf("\tProtocol is T=1\n");
      } else
      {
        printf("\tProtocol is T=0\n");
      }

      /* Activated card type */
      switch (type)
      {
        case 0:
          break;                /* Not an authorized value */
        case 0x02:
          printf("\tAsynchronous card, 3.6864MHz clock\n");
          break;
        case 0x12:
          printf("\tAsynchronous card, 7.3728MHz clock\n");
          break;
        default:
          printf("\tUnknown card type %02X\n", type);
          break;
      }

      if (config != NULL)
      {
        /* Display communication parameters */
        printf("\tTA1 (FI & DI)   = %02X\n", config[0]);
        printf("\tTC1 (EGT)       = %02X\n", config[1]);
        if (stat & 0x80)
        {
          printf("\tIFSC            = %02X\n", config[2]);
          printf("\tTB3 (BWI & CWI) = %02X\n", config[3]);
        } else
        {
          printf("\tWI              = %02X\n", config[2]);
        }
      }
    } else if (slot)
    {
      /* On SIM/SAM slot card is "always inserted" */
      printf("\tCard position not available on SIM/SAM connector\n");
    } else
    {
      /* On main slot we have a working card presence switch */
      printf("\tCard inserted but not powered\n");
    }
  } else
  {
    printf("\tNo smartcard\n");
  }
}

int main(int argc, char **argv)
{
  SWORD rc;
  int i;
  char s_buffer[64];
  DWORD features;
  BYTE buffer[256];
  WORD buflen;
  BYTE atr[32];
  WORD atrlen;
  BYTE slot, stat, type;
  BYTE config[4];

  char *device = NULL;

  if (argc > 1)
    device = argv[1];

  printf("SpringCard SDK for K531/SpringProx-CF/CSB Legacy\n");
  printf("\n");
  printf("SMARTCARD reference demo\n");
  printf("------------------------\n");
  printf("www.springcard.com\n\n");
  printf("This sample can be used only with SpringCard products featuring\n");
  printf("a GemPlus GemCore smartcard chipset (CSB5, SpringProx-RC, ...)\n");
  printf("\n");

  rc = SPROX_GetLibrary(s_buffer, sizeof(s_buffer));
  if (rc != MI_OK)
  {
    printf("Failed to get API version\n");
    goto done;
  }
  printf("API version : %s\n", s_buffer);
  printf("\n");

  /* Open reader */
  /* ----------- */

  rc = SPROX_ReaderOpen(device);
  if (rc != MI_OK)
  {
    if (device != NULL)
      printf("Reader not found on %s\n", device);
    else
      printf("Reader not found\n");
    goto done;
  }

  rc = SPROX_ReaderGetDevice(s_buffer, sizeof(s_buffer));
  if (rc == MI_OK)
    printf("Reader found on %s\n", s_buffer);

  rc = SPROX_ReaderGetFirmware(s_buffer, sizeof(s_buffer));
  if (rc == MI_OK)
    printf("Reader firmware is %s\n", s_buffer);

  rc = SPROX_ReaderGetFeatures(&features);
  if ((rc == MI_OK) && !(features & 0x00000020))
  {
    printf("Warning: this reader has no support for smartcard\n");
    printf("         all subsequent tests will fail !\n");
  }

  /* Check that reader has a GemCore smartcard reader */
  /* ------------------------------------------------ */
  rc = SPROX_Card_GetFirmware(s_buffer, sizeof(s_buffer));
  if (rc == MI_FUNCTION_NOT_AVAILABLE)
  {
    printf("Smartcard reader not available\n");
    rc = MI_OK;
    goto close;
  }
  if (rc != MI_OK)
  {
    printf("Failed to access smartcard reader\n");
    goto close;
  }

  printf("Smartcard reader is %s\n", s_buffer);

  /* Enumerate the available slots */
  /* ----------------------------- */
  for (slot = 0; slot < 16; slot++)
  {
    rc = SPROX_Card_Status(slot, &stat, NULL, NULL);
    if (rc == MI_OK)
    {
      printf("- Found smartcard slot %d ", slot);
      if (slot == 0)
      {
        printf("(main ISO slot)");
      } else
      {
        printf("(SIM/SAM slot)");
      }
      printf("\n");
      show_status(slot, stat, 0, NULL);
    } else if (rc == -214)
    {
      printf("  slot %d is not wired\n", slot);
    } else if (rc == -216)
    {
      printf("  slot %d does not exist\n", slot);
    } else
    {
      printf("Failed to get status for slot %d\n", slot);
      goto close;
    }
  }

  /* For each available slot, try to power up the card, and on success performs an */
  /* ISO 7816-4 "Select master file" command. Then power down the card.            */
  for (slot = 0; slot < 16; slot++)
  {
    rc = SPROX_Card_Status(slot, NULL, NULL, NULL);
    if ((rc == -214) || (rc == -216))
      continue;                 /* No such slot */

    if (rc != MI_OK)
    {
      printf("Failed to get status for slot %d\n", slot);
      goto close;               /* Error */
    }

    /* Power up the card */
    /* ----------------- */

    /* Don't care whether the smartcard is already powered or not, */
    /* the PowerUP function at least reset it                      */
    atrlen = sizeof(atr);
    rc = SPROX_Card_PowerUp_Auto(slot, atr, &atrlen);

    if ((rc == -5) || (rc == -94) || (rc == -93))
    {
      printf("No smartcard in slot %d\n", slot);
      continue;
    }

    if (rc != MI_OK)
    {
      printf("Failed to power up card in slot %d\n", slot);
      goto close;               /* Other error */
    }

    printf("Connected to smartcard in slot %d\n", slot);
    printf("\tATR : ");
    for (i = 0; i < atrlen; i++)
      printf("%02X", atr[i]);
    printf("\n");

    /* Get updated card status */
    /* ----------------------- */
    rc = SPROX_Card_Status(slot, &stat, &type, config);
    if (rc != MI_OK)
    {
      printf("Failed to get status for slot %d\n", slot);
      goto close;               /* Error ? */
    }

    show_status(slot, stat, type, config);

    /* Send the select MF APDU */
    /* ----------------------- */
    buffer[0] = 0x00;           /* CLA */
    buffer[1] = 0xA4;           /* INS : select file */
    buffer[2] = 0x00;           /* P1  */
    buffer[3] = 0x00;           /* P2  */
    buffer[4] = 0x02;           /* Lc  */
    buffer[5] = 0x3F;
    buffer[6] = 0x00;           /* Command data = 3F00 */

    buflen = sizeof(buffer);
    rc = SPROX_Card_Exchange(slot, buffer, 7, buffer, &buflen);
    if (rc != MI_OK)
    {
      printf("Failed to exchange APDU with card in slot %d\n", slot);
      goto close;               /* Error ? */
    }

    printf("Select MF APDU returned %d bytes\n", buflen);
    if (buflen > 2)
    {
      /* Some data in the response ? */
      printf("\tData : ");
      for (i = 0; i < buflen - 2; i++)
        printf("%02X", buffer[i]);
      printf("\n");
    }
    if (buflen >= 2)
    {
      /* Echo SW1 and SW2 */
      printf("\tSW   : %02X%02X\n", buffer[buflen - 2], buffer[buflen - 1]);
    }

    if (buffer[buflen - 2] == 0x61)
    {
      /* SW1=61 : we have to perform a Get Response to retrieve SW2 bytes of data */
      BYTE t = buffer[buflen - 1];  /* t <- SW2 */

      buffer[0] = 0x00;         /* CLA */
      buffer[1] = 0xC0;         /* INS : get response */
      buffer[2] = 0x00;         /* P1  */
      buffer[3] = 0x00;         /* P2  */
      buffer[4] = t;            /* Le  */

      buflen = sizeof(buffer);
      rc = SPROX_Card_Exchange(slot, buffer, 5, buffer, &buflen);
      if (rc != MI_OK)
      {
        printf("Failed to exchange APDU with card in slot %d\n", slot);
        goto close;             /* Error ? */
      }

      printf("Get Response APDU returned %d bytes\n", buflen);
      if (buflen > 2)
      {
        /* Some data in the response ? */
        printf("\tData : ");
        for (i = 0; i < buflen - 2; i++)
          printf("%02X", buffer[i]);
        printf("\n");
      }
      if (buflen >= 2)
      {
        /* Echo SW1 and SW2 */
        printf("\tSW   : %02X%02X\n", buffer[buflen - 2], buffer[buflen - 1]);
      }

    }

    /* Power down the card */
    /* ------------------- */

    rc = SPROX_Card_PowerDown(slot);
    if (rc != MI_OK)
    {
      printf("Failed to power down card in slot %d\n", slot);
      goto close;               /* Error ? */
    }
  }

  rc = 0;

close:
  /* Close the reader */
  SPROX_ReaderClose();

done:
  /* Display last error */
  if (rc == MI_OK)
  {
    printf("Done\n");
  } else
  {
    printf("%s (%d)\n", SPROX_GetErrorMessage(rc), rc);
  }
  return 0;
}
